#include "../Headers/tTime.hpp"
#include "../Headers/tLogger.hpp"

#include <sstream>
#include <iomanip>

using namespace std;

int TStopWatch::currentSWLv = 0;
int TStopWatch::levelBlocked = 65536;

TGlobalStopWatches::TStopWatchWithoutLv TGlobalStopWatches::sw[256] = {};

string TTime_int2str(int val) {
	string s;
	stringstream ss(s);
	ss << val;
	return ss.str();
}

TStopWatch::TStopWatch() {
	processName = '\n';
	level = currentSWLv++;
	flagAddedLavel = true;
	flagPrintEnable = true;
	lastTime = clock();

#ifdef FLAG_MUTE_STOPWATCH
	setFlagMuted(true);
#endif
}

TStopWatch::TStopWatch(bool flagPrintEnableIn) {
	processName = '\n';
	if (flagPrintEnableIn) {
		level = currentSWLv++;
		flagAddedLavel = true;
	}
	flagPrintEnable = flagPrintEnableIn;
	lastTime = clock();
#ifdef FLAG_MUTE_STOPWATCH
	setFlagMuted(true);
#endif
}

TStopWatch::TStopWatch(std::string processNameNew, bool flagPrintEnableIn, bool flagAddLevel) {
	if (flagAddLevel) {
		level = currentSWLv++;
		flagAddedLavel = true;
	}
	flagPrintEnable = flagPrintEnableIn;

#ifdef FLAG_MUTE_STOPWATCH
	setFlagMuted(true);
#endif
	start(processNameNew);
}

TStopWatch::~TStopWatch() {
	stop();
	if (flagAddedLavel) {
		currentSWLv--;
	}
}

void TStopWatch::setFlagMuted(bool newFlag) {
	flagMuted = newFlag;
}

void TStopWatch::blockLowerLvs() {
	levelBlocked = level + 1;
}

void TStopWatch::setSimuMode() {
	flagSimuClockMode = true;
	simuTime = 0;
}

void TStopWatch::simuModeAddTimeMS(int addTimeMS) {
	simuTime += addTimeMS;
}

void TStopWatch::setTime(int newTime) {
	flagDesignatedTime = true;
	simuTime = newTime;
}

void TStopWatch::start(std::string processNameNew) {
	lastTime = clock();
	if (flagPrintEnable) {
		if (processNameNew.compare("\n") == 0) {
			flagMuted = true;
		}
		processName = processNameNew;
	}
	else {
		processName = '\n';
	}
}
int TStopWatch::stop(std::string processNameNew, bool forceDisplay) {
	int ret = -1;
	if (flagMuted && (!forceDisplay)) {
		return ret;
	}
	if ((level >= levelBlocked || (!flagPrintEnable)) && (!forceDisplay)) {
		return  ret;
	}
	clock_t newTime = clock();
	ret = newTime - lastTime;
	std::string content;
	if (processName.length() > 0 && processName[0] != '\n') {
		if (processName.compare("[ControllerWrap] Connect encoder") == 0) {

		}
		content += "Process -";
		for (int idx = 0; idx < level; idx++)
			content += "|-";
		content += "> " + processName + " < endded with ";
#ifndef PLATFORM_WIN

		int val = int((newTime - lastTime) * (1000 / CLOCKS_PER_SEC));
		string s;
		stringstream ss(s);
		ss << val;

		content += ss.str() + "ms passed\n";
#else
		ret = newTime - lastTime;
		content += Formater::int2str(ret) + "ms passed";
#endif
		TLogger::writeLine(content);
	}
	lastTime = newTime;
	processName = processNameNew;
	return ret;
}

void TStopWatch::updateNameAndStop(std::string processNameOverwrite, std::string processNameNext) {
	processName = processNameOverwrite;
	stop(processNameNext);
}

bool TStopWatch::shoudFire(int timeout) {
	return (clock() - lastTime) > timeout;
}

int TStopWatch::getTimeRemain(int timeout) {
	clock_t tPass = clock() - lastTime;
	if (tPass > timeout) {
		return 0;
	}
	else {
		return timeout - tPass;
	}
}

bool TStopWatch::flagDesignatedTime = false;
bool TStopWatch::flagSimuClockMode = false;
int64_t TStopWatch::simuTime = 0;

time_t TStopWatch::getUnixTime() {
	return time(0);
}

clock_t TStopWatch::getTimeSinceStart() {
	if (flagDesignatedTime) {
		return simuTime;
	}
	else if (flagSimuClockMode) {
		simuTime += 100;
		return simuTime;
	}
	else {
		return getRealTimeSinceStart();
	}
}

clock_t TStopWatch::getRealTimeSinceStart() {
	return clock();
}

string TStopWatch::getTimeSinceStartAsString(int nDigit) {
	unsigned long realTime = getRealTimeSinceStart();

	if (nDigit <= 0) {
		std::stringstream ss;
		ss << realTime;
		return ss.str();
	}
	else {
		std::ostringstream oss;
		oss << std::setfill('0') << std::setw(nDigit) << realTime;
		return oss.str();
	}

	// every byte takes no more than 3 digits (x256)
	//return Formater::int2str<unsigned long>(realTime, nDigit);
}

void TStopWatch::getDate(string& year, string& month, string& day) {
	time_t ttNow = time(0);
	tm ptmNow;
	localtime_s(&ptmNow, &ttNow);
	year = TTime_int2str(1900 + ptmNow.tm_year);
	if (ptmNow.tm_mon < 9)
		//Fill in the leading 0 if less than 10
		month = "0" + TTime_int2str(1 + ptmNow.tm_mon);
	else
		month = TTime_int2str(1 + ptmNow.tm_mon);

	if (ptmNow.tm_mday < 10)
		day = "0" + TTime_int2str(ptmNow.tm_mday);
	else
		day = TTime_int2str(ptmNow.tm_mday);
}

std::string TStopWatch::getDate() {
	string year, month, day;
	getDate(year, month, day);
	return year + month + day;
}

TGlobalStopWatches::TStopWatchWithoutLv::TStopWatchWithoutLv() {
	_core = new TStopWatch("", true, false);
}

int TGlobalStopWatches::start(int idx, std::string processNameNext) {
	sw[idx]._core->start(processNameNext);
	return 1;
}

int TGlobalStopWatches::stop(int idx, std::string processNameNext) {
	return sw[idx]._core->stop(processNameNext);
}

int TGlobalStopWatches::setFlagMuted(int idx, bool newFlag) {
	sw[idx]._core->setFlagMuted(newFlag);
	return 1;
}

#ifdef PLATFORM_LINUX
stopWatchMTLinux::stopWatchMTLinux(int levelSet) {
	level = levelSet;
}
void stopWatchMTLinux::start(std::string processNameNew) {
	gettimeofday(&tStart, 0);
	processName = processNameNew;
}
void stopWatchMTLinux::stop(std::string processNameNew) {
	//if (!FLAG_MUTE_STOPWATCH) {
	if (processName[0] != '\n') {
		cout << "Process -";
		for (int idx = 0; idx < level; idx++)
			cout << "|-";
		cout << "> " << processName << " < endded with ";
	}
	gettimeofday(&cTime, 0);
	cTime.tv_sec -= tStart.tv_sec;
	cTime.tv_usec -= tStart.tv_usec;
	tPassed = 1000000LL * cTime.tv_sec + cTime.tv_usec;
	tPassed /= 1000;


	cout << tPassed << "ms passed" << endl;

	//}
	gettimeofday(&tStart, 0);
	processName = processNameNew;
}
#endif